/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2018-2021.
 * Description: add hert1380 cfi driver adapation
 * Author: xiaoqian <xiaoqian9@huawei.com>
 * Create: 2018-10-09
 */

#include "cfi_enhance_debug.h"
/*
 * Log the map and chip info to debug.
 * We may log the chip reg with polling methoed in the future
 * to get more useful info
 */
void cfi_enhance_report_state(struct map_info *map, struct flchip *chip)
{
	cfi_enhance_debug_log("map info: name %s, size %lu, phys %lx\n",
			map->name, map->size, (unsigned long int)map->phys);
	cfi_enhance_debug_log("flchip info: start %lx, state %x, oldstate %x\n",
			chip->start, chip->state, chip->oldstate);
}


/*
 * Return true if the chip erase have the correct value. Make sure the chip
 * state is ready before.
 *
 * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
 * non-suspended sector) and it is indicated by no bits toggling.
 *
 * Error are indicated by toggling bits or bits held with the wrong value,
 * or with bits toggling.
 *
 * Note that anything more complicated than checking if no bits are toggling
 * (including checking DQ5 for an error status) is tricky to get working
 * correctly and is therefore not done	(particulary with interleaved chips
 * as each chip must be checked independently of the others).
 *
 */
int __xipram cfi_enhance_erase_good(struct map_info *map, struct flchip *chip, unsigned long addr, int len)
{
	map_word curd, expected;
	int size = 0;
	struct cfi_private *cfi = map->fldrv_priv;

	expected = map_word_ff(map);

	cfi_enhance_soft_reset(chip, map, cfi);

	while (size < len) {
		curd = map_read(map, addr + size);
		if (!map_word_equal(map, curd, expected)) {
			cfi_enhance_debug_log("Task %s(%u) erase failed at %pK !\n ",
					current->comm, current->pid, (void *)(uintptr_t)(addr + size));
			cfi_enhance_report_state(map, chip);
			return 0;
		}
		size += map_bankwidth(map);
	}
	return 1;
}

/*
 * Return true if the chip buffer have the correct value. Make sure the chip
 * state is ready before.
 *
 * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
 * non-suspended sector) and it is indicated by no bits toggling.
 *
 * Error are indicated by toggling bits or bits held with the wrong value,
 * or with bits toggling.
 *
 * Note that anything more complicated than checking if no bits are toggling
 * (including checking DQ5 for an error status) is tricky to get working
 * correctly and is therefore not done	(particulary with interleaved chips
 * as each chip must be checked independently of the others).
 *
 */
static int __xipram cfi_enhance_buffer_good(struct map_info *map, unsigned long addr, const u_char *buf, int len)
{
	map_word curd, datum;
	int size = 0, words = len / map_bankwidth(map);

	while (size < words * map_bankwidth(map)) {
		datum = map_word_load(map, buf + size);
		curd = map_read(map, addr + size);
		if (!map_word_equal(map, curd, datum))
			return 0;
		size += map_bankwidth(map);
	}
	return 1;
}


/*
 * Return 0 if all buffer correct,or return -EIO
 *
 * This function will check buffers for three times to
 * make sure buffers writing successfully.
 * it will delay 10us and 250us on the first and second failues
 */
int __xipram cfi_enhance_buffer_write_check(struct map_info *map,
					    struct flchip *chip,
					    unsigned long addr,
					    const u_char *buf, int len)
{
	struct cfi_private *cfi = map->fldrv_priv;
	int retrycnt = 0;

	cfi_enhance_soft_reset(chip, map, cfi);

	for (retrycnt = 0 ; retrycnt < 3 ; retrycnt++) {
		if (!cfi_enhance_buffer_good(map, addr, buf, len)) {
			if (retrycnt == 0)
				udelay(10); /* delay 10us for the first failure */
			else if (retrycnt == 1)
				udelay(250); /* delay 250us for the first failure */
			else {
				/* after three times,it's true failure, reset the chip and report log */
				cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
						cfi->device_type, NULL);
				cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
						cfi->device_type, NULL);
				cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi,
						cfi->device_type, NULL);
				cfi_enhance_debug_log("Task %s(%u) write %d buffers at %pK failed and return -EIO!\n ",
						current->comm, current->pid, len, (void *)(uintptr_t)addr);
				cfi_enhance_report_state(map, chip);
				return -EIO;
			}
		}
	}
	return 0;
}
